import {
  ARRAY_TYPES,
  EmitChange,
  FormEventType,
  HandleChange,
  Maskable,
  TYPE_BOOL,
  TYPE_DOUBLE,
  TYPE_EXPRESSION,
  TYPE_GROUP,
  TYPE_INT,
  TYPE_MATERIAL_SET,
  TYPE_OPTION,
  TYPE_SET_ELEMENT,
  TYPE_SET_NODE,
  TYPE_SET_SURFACE,
  TYPE_SET_REFER_POINT,
  TYPE_TABULAR,
} from '@app-nextmesh/types';
import { events } from '@app-nextmesh/utils';
import { Card } from 'antd';
import { isEmpty, isPlainObject } from 'lodash';
import { useEffect, useRef, useState } from 'react';
import {
  ArrayN,
  CheckBox,
  ElementSetSelect,
  Entity,
  ErrorTips,
  ExpressionInput,
  FormField,
  FormNativeSelect,
  Group,
  MaterialSelect,
  Tabular,
  ValueField,
} from '.';
import ObjectType from '../ObjectType';
import { IJSONFormProps } from './types';
import { getLabel, isEntityTypes, makeDefaultFormItem } from './utils';

/**
 * 通过formFormat和formData等生成Form表单项
 * @param
 * @returns
 */
export default function JSONFormField2({
  readonly,
  initName: formName,
  formFormat,
  formData,
  onChange,
  indicesAssigned,
  switchedOnEntityGroup,
  onSwitchEntityGroup,
}: IJSONFormProps) {
  const formDataRef = useRef(formData);
  const [form, setForm] = useState(undefined);
  const [maskableChecked, setMaskableChecked] = useState(false);
  const [show, setShow] = useState(true);

  useEffect(() => {
    const newForm = formData ?? makeDefaultFormItem(formFormat);
    formDataRef.current = newForm;
    setForm(newForm);
  }, [formData]);
  useEffect(() => {
    if (formFormat[Maskable]) {
      setMaskableChecked(formData != null);
    }
  }, [formFormat, formData]);
  useEffect(() => {
    if (!formFormat[HandleChange]) {
      return () => {};
    }
    events.on(FormEventType.TypeChanged, handleFormChangeEvent);
    return () => {
      events.off(FormEventType.TypeChanged, handleFormChangeEvent);
    };
  }, [formFormat]);
  useEffect(() => {
    if (!formFormat[EmitChange]) {
      return;
    }
    const value = formData?.Type;
    if (formData) {
      // TODO hack it，待优化；在整个表单都渲染之后，再触发
      setTimeout(() =>
        events.emit(FormEventType.TypeChanged, [formName, value]),
      );
    }
  }, [formName, formFormat, formData]);
  const handleFormChangeEvent = (names: string[]) => {
    if (!names || names.length === 0) {
      return;
    }
    let handleChange = formFormat[HandleChange];
    if (handleChange[names[0]]) {
      let changeShow: boolean = false;
      for (const name of names) {
        handleChange = handleChange[name];
        if (!handleChange) {
          changeShow = false;
          break;
        }
        changeShow = true;
      }
      if (changeShow) {
        onChange(formName, formDataRef.current);
      } else {
        onChange(formName, undefined);
      }
      setShow(changeShow);
    }
  };

  if (form == null) {
    return <></>;
  }
  if (!isPlainObject(formFormat)) {
    return (
      <ErrorTips>{`${formName}的值格式错误，必须是一个JSON对象`}</ErrorTips>
    );
  }
  if (!Object.prototype.hasOwnProperty.call(formFormat, 'Type')) {
    return <ErrorTips>{`${formName}缺少Type字段`}</ErrorTips>;
  }

  const handleChange = (value: any) => {
    if (isPlainObject(value) && value.Value != null) {
      if (value.Unit == null) {
        formDataRef.current = value.Value;
      } else {
        formDataRef.current = value;
      }
    } else {
      formDataRef.current = value;
    }
    onChange(formName, formDataRef.current);
  };

  const type = formFormat.Type; // 每项首先有一个 Type 字段
  const maskable = formFormat.Maskable; // 是否可被隐藏
  const formatUnit = formFormat.Unit;
  let label: string = formName;
  if (Object.prototype.hasOwnProperty.call(formFormat, 'Label')) {
    label = formFormat.Label;
  }
  if (label) {
    label = getLabel(label);
  }
  if (!type) {
    return (
      <ErrorTips>{`${formName}的Type:${type}，是无效的Type类型`}</ErrorTips>
    );
  }

  let component: JSX.Element;
  let localForm = form;
  if (!isPlainObject(localForm)) {
    localForm = { Type: type, Value: localForm };
  }
  if (isEntityTypes(type)) {
    // 选择顶点、边、面、体
    component = (
      <Entity
        disabled={readonly}
        name={formName}
        switchedOn={formName === switchedOnEntityGroup}
        type={type}
        label={label}
        value={localForm.Value}
        assignedValues={indicesAssigned}
        onChange={handleChange}
        onClick={onSwitchEntityGroup}
      />
    );
  } else if (ARRAY_TYPES.includes(type)) {
    // 输入多个数值类型
    component = (
      <ArrayN
        disabled={readonly}
        type={type}
        label={label}
        value={localForm.Value}
        units={formatUnit}
        onChange={handleChange}
      />
    );
  } else if ([TYPE_DOUBLE, TYPE_INT, TYPE_BOOL].includes(type)) {
    // 数值类型
    component = (
      <ValueField
        disabled={readonly}
        type={type}
        label={label}
        value={localForm}
        units={formatUnit}
        onChange={handleChange}
      />
    );
  } else if (type === TYPE_EXPRESSION) {
    component = (
      <FormField label={label}>
        <ExpressionInput
          value={localForm}
          disabled={readonly}
          onChange={handleChange}
          units={formatUnit}
        />
      </FormField>
    );
  } else if (type === TYPE_TABULAR) {
    component = (
      <Tabular
        formFormat={formFormat}
        disabled={readonly}
        value={localForm.Value}
        onChange={handleChange}
      />
    );
  } else if (
    type === TYPE_SET_NODE ||
    type === TYPE_SET_ELEMENT ||
    type === TYPE_SET_SURFACE ||
    type === TYPE_SET_REFER_POINT
  ) {
    component = (
      <FormField label={label}>
        <ElementSetSelect
          disabled={readonly}
          value={localForm.Value}
          onChange={handleChange}
        />
      </FormField>
    );
  } else if (type === TYPE_MATERIAL_SET) {
    component = (
      <FormField label={label}>
        <MaterialSelect
          disabled={readonly}
          value={localForm.Value}
          onChange={handleChange}
        />
      </FormField>
    );
  } else if (type === TYPE_OPTION) {
    component = (
      <FormField label={label}>
        <FormNativeSelect
          disabled={readonly}
          value={localForm.Value}
          options={formFormat.Options.map((option) => {
            return { value: option, label: getLabel(option) };
          })}
          onChange={handleChange}
        />
      </FormField>
    );
  } else if (type === TYPE_GROUP) {
    component = (
      <Group
        readonly={readonly}
        label={label}
        typeObject={formFormat}
        form={localForm.Value}
        onInputChange={handleChange}
      />
    );
  } else if (isPlainObject(type) && !isEmpty(type)) {
    // 根据 form.Type 的值，获取或生成默认的完整formData，
    // 传递给JSONFormatForm组件，生成表单界面
    /**
     * type 格式示例：
      {
        "Hard": {},
        "Linear": {
          "c_0": {
            "Type": "double",
            "Default": 0.0
          },
          "K": {
            "Type": "double",
            "Default": 0.0
          },
          "\\sigma_\\infty": {
            "Type": "double",
            "Default": 0.0
          }
        }
      }
     */
    component = (
      <ObjectType
        readonly={readonly}
        formFormat={formFormat}
        form={form}
        onInputChange={handleChange}
        indicesAssigned={indicesAssigned}
        switchedOnEntityGroup={switchedOnEntityGroup}
        handleSwitchEntityGroup={onSwitchEntityGroup}
      />
    );
  } else {
    component = (
      <ErrorTips>{`${formName}的Type:${type}，无效的Type类型`}</ErrorTips>
    );
  }
  if (maskable) {
    const handelCheckChange = (checked: boolean) => {
      setMaskableChecked(checked);
      const value = checked ? form : undefined;
      handleChange(value);
    };

    component = (
      <Card styles={{ body: { padding: 5 } }}>
        <CheckBox
          disabled={readonly}
          label={label}
          checked={maskableChecked}
          onChange={handelCheckChange}
        />
        {maskableChecked && component}
      </Card>
    );
  } else {
    // 非maskable，如果是对象，显示卡片；否则会导致多层嵌套卡片
    if (isPlainObject(type) && !isEmpty(type)) {
      component = (
        <Card styles={{ body: { padding: 5 } }}>
          <label>{label}</label>
          {component}
        </Card>
      );
    }
  }
  return show && component;
}
